home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / X11 / wais / waisgate / irfileio.c < prev    next >
C/C++ Source or Header  |  1995-05-09  |  19KB  |  805 lines

  1. /* WIDE AREA INFORMATION SERVER SOFTWARE:
  2.    No guarantees or restrictions.  See the readme file for the full standard
  3.    disclaimer.
  4.  
  5.    Brewster@think.com
  6. */
  7.  
  8. /* Change log:
  9.  * $Log:    irfileio.c,v $
  10.  * Revision 1.12  92/04/12  17:30:21  jonathan
  11.  * Fixed WriteString to quote backslash as well as double-quotes.  Thanks to
  12.  * dxl0243@hertz.njit.edu (Dong Liu).
  13.  * 
  14.  * Revision 1.11  92/03/06  10:57:16  jonathan
  15.  * removed extraneous ; after function WriteTM.
  16.  * 
  17.  * Revision 1.10  92/03/04  19:20:33  jonathan
  18.  * Made SkipObject return EOF if at end of file.
  19.  * 
  20.  * Revision 1.9  92/02/16  21:08:37  jonathan
  21.  * Changed a few more char ch to long ch (for return value of fgetc).
  22.  * 
  23.  * Revision 1.8  92/02/12  13:24:13  jonathan
  24.  * Added "$Log" so RCS will put the log message in the header
  25.  * 
  26. */
  27.  
  28. /********************************************************
  29.  *  Writing and reading structures to files.        *
  30.  *                            *
  31.  *  These use the Lisp printer format with the        *
  32.  *  lisp naming conventions.  You ask: "Why would    *
  33.  *  you want to use that?".  Well, we need an        *
  34.  *  easily readable data syntax that can handle        *
  35.  *  a large number of different data types.        *
  36.  *  Further, we need it to be tagged so that        *
  37.  *  run time tagged languages can read it and         *
  38.  *  it is flexible.  We need one that supports        *
  39.  *  optional fields so that the format can         *
  40.  *  grow backcompatibly.  And (the kicker),        *
  41.  *  it must be read from many languages since        *
  42.  *  user interfaces may be written in anything        *
  43.  *  from smalltalk to hypercard.            *
  44.  *                             *
  45.  *  -brewster 5/10/90                    *
  46.  ********************************************************/
  47.  
  48. #include <string.h>
  49. #include <ctype.h>
  50. #include "irfileio.h"
  51. #include "cutil.h"
  52.  
  53. #define INDENT_SPACES 2
  54. #define MAX_INDENT 40
  55. static long indent_level;  /* this is the number of indent levels */
  56.  
  57.  
  58. /**********************/
  59. /*  WRITING TO FILES  */
  60. /**********************/
  61.  
  62.  
  63. static void indent _AP((FILE* file));
  64.  
  65. static void indent(file)
  66. FILE* file;
  67. /* indent the right number of spaces.  make sure that indent_level is 
  68.  * non-negative, and that it does not indent too much
  69.  */
  70. {
  71.   long i;
  72.   for(i = 0; i <= MIN(MAX_INDENT, MAX(0L, indent_level * INDENT_SPACES)); i++){
  73.     putc(' ', file);
  74.   }
  75. }
  76.  
  77. long WriteStartOfList(file)
  78. FILE* file;
  79. {
  80.   indent_level++;
  81.   return(fprintf(file, " ( "));
  82. }
  83.  
  84. long WriteEndOfList(file)
  85. FILE* file;
  86. {
  87.   indent_level--;
  88.   return(fprintf(file, " ) "));
  89. }
  90.  
  91. long WriteStartOfStruct(name,file)
  92. char* name;
  93. FILE* file;
  94. {
  95.   indent_level++;
  96.   return(fprintf(file, " (:%s ", name));
  97. }
  98.  
  99. long WriteEndOfStruct(file)
  100. FILE* file;
  101. {
  102.   indent_level--;
  103.   return(fprintf(file, " ) "));
  104. }
  105.  
  106. long WriteSymbol(name,file)
  107. char* name;
  108. FILE* file;
  109. {
  110.   return(fprintf(file, " %s ", name));
  111. }
  112.  
  113. long WriteNewline(file)
  114. FILE* file;
  115. {
  116.   long return_value = fprintf(file, "\n");
  117.   indent(file);
  118.   return(return_value);
  119. }
  120.  
  121. long WriteLong(number,file)
  122. long number;
  123. FILE* file;
  124. {
  125.   return(fprintf(file, " %ld ", number));  
  126. }
  127.  
  128. long WriteDouble(number,file)
  129. double number;
  130. FILE* file;
  131. {
  132.   return(fprintf(file, " %f ", number));
  133. }
  134.  
  135. long WriteString(string,file)
  136. char* string;
  137. FILE* file;
  138. {
  139.   long i;
  140.   putc('\"', file);
  141.   for(i = 0; i < strlen(string); i++){
  142.     if(string[i] == '\\' || string[i] == '\"')
  143.       putc('\\', file);        /* quote the string quotes going into the file */
  144.     putc(string[i], file);
  145.   }
  146.   putc('\"', file);
  147.   return(1);
  148. }
  149.  
  150. long WriteAny(value,file)
  151. any* value;
  152. FILE* file;
  153. {
  154.   WriteStartOfStruct("any", file);
  155.   WriteSymbol(":size", file); WriteLong(value->size, file);
  156.   WriteSymbol(":bytes", file);
  157.   Write8BitArray(value->size, value->bytes, file);
  158.   return(WriteEndOfStruct(file));
  159. }
  160.  
  161. long Write8BitArray(length,array,file)
  162. long length;
  163. char* array;
  164. FILE* file;
  165. {
  166.   long i;
  167.   fprintf(file, " #( ");
  168.   for(i=0; i<length; i++){
  169.     WriteLong((long)array[i], file);
  170.   }
  171.   return(fprintf(file, " ) "));
  172. }
  173.  
  174. /* Writes a time object to a file */
  175. long WriteTM(atime,file)
  176. struct tm* atime;
  177. FILE* file;
  178. {
  179.   WriteStartOfStruct("tm", file);
  180.   WriteSymbol(":tm-sec", file); WriteLong(atime->tm_sec, file);
  181.   WriteSymbol(":tm-min", file); WriteLong(atime->tm_min, file);
  182.   WriteSymbol(":tm-hour", file); WriteLong(atime->tm_hour, file);
  183.   WriteSymbol(":tm-mday", file); WriteLong(atime->tm_mday, file);
  184.   WriteSymbol(":tm-mon", file); WriteLong(atime->tm_mon, file);
  185.   WriteNewline(file);
  186.   WriteSymbol(":tm-year", file); WriteLong(atime->tm_year, file);
  187.   WriteSymbol(":tm-wday", file); WriteLong(atime->tm_wday, file);
  188.   WriteNewline(file);
  189.   WriteSymbol(":tm-yday", file); WriteLong(atime->tm_yday, file);
  190.   WriteSymbol(":tm-isdst", file); WriteLong(atime->tm_isdst, file);
  191.   WriteEndOfStruct(file);
  192.   return(WriteNewline(file));
  193. }
  194.  
  195. Boolean  
  196. writeAbsoluteTime(atime,file)
  197. struct tm* atime;
  198. FILE* file;
  199. {
  200.   WriteStartOfStruct("absolute-time",file);
  201.   WriteNewline(file);
  202.   WriteSymbol(":year",file); WriteLong((long)atime->tm_year,file);
  203.   WriteNewline(file);
  204.   WriteSymbol(":month",file); WriteLong((long)atime->tm_mon,file);
  205.   WriteNewline(file);
  206.   WriteSymbol(":mday",file); WriteLong((long)atime->tm_mday,file);
  207.   WriteNewline(file);
  208.   WriteSymbol(":hour",file); WriteLong((long)atime->tm_hour,file);
  209.   WriteNewline(file);
  210.   WriteSymbol(":minute",file); WriteLong((long)atime->tm_min,file);
  211.   WriteNewline(file);
  212.   WriteSymbol(":second",file); WriteLong((long)atime->tm_sec,file);
  213.   WriteNewline(file);
  214.   return(WriteEndOfStruct(file));
  215. }
  216.  
  217.  
  218.  
  219.  
  220. /************************/
  221. /*  READING FROM FILES  */
  222. /************************/
  223.  
  224.  
  225. /* these are states of the parser */
  226. #define BEFORE 1
  227. #define DURING 2
  228. #define HASH 3
  229. #define S 4
  230. #define QUOTE 5
  231.  
  232. /* returns TRUE if it hits an '(' before hitting any non whitespace.
  233.    It has an added hack to detect NIL: it looks to make sure that it is nil,
  234.    and then ungetc's a \) on the stream so that the end-checkers will catch it.
  235.    Quack. */
  236. Boolean ReadStartOfList(file)
  237. FILE* file;
  238. {
  239.   long ch;
  240.   while(TRUE){
  241.     ch = getc(file);
  242.     if(ch == '(') 
  243.       return(TRUE);
  244.     if(!isspace(ch)){
  245.       /* check for NIL */
  246.       if(ch == 'N' || ch == 'n'){
  247.     ch = getc(file);
  248.     if(ch == 'I' || ch == 'i'){
  249.       ch = getc(file);
  250.       if(ch == 'L' || ch == 'l'){
  251.         ungetc(')', file);
  252.         return(TRUE);
  253.       }
  254.     }
  255.       }
  256.       return(FALSE); /* not NIL */
  257.     }
  258.   }
  259. }
  260.  
  261.  
  262. /* returns TRUE if it hits an ')' before hitting any non whitespace*/
  263. Boolean ReadEndOfList(file)
  264. FILE* file;
  265. {
  266.   long ch;
  267.   while(TRUE){
  268.     ch = getc(file);
  269.     if(ch == ')') 
  270.       return(TRUE);
  271.     if(!isspace(ch)) 
  272.       return(FALSE);
  273.   }
  274. }
  275.  
  276. #define STRING_ESC '\\'
  277.  
  278. long 
  279. SkipObject(file)
  280. FILE* file;
  281. /* read an object of unknown type out of the file.  We handle:
  282.       strings
  283.       longs, doubles, and symbols
  284.       structs, lists, and arrays
  285.    and anything composed of them (absolute time etc)
  286. */
  287. {
  288.   long ch;
  289.  
  290.   while (true)    
  291.     { ch = getc(file);
  292.       if (ch == EOF)
  293.     return (EOF); /* we are done */
  294.       else
  295.     { if (isspace(ch))
  296.         continue; /* skip this char */
  297.       else if (ch == '"') /* string */
  298.         { long escapeCount = 0;
  299.           while (true)
  300.         { ch = getc(file);
  301.           if (ch == EOF)
  302.             return (EOF);
  303.           else
  304.             { if (ch == STRING_ESC)
  305.             { escapeCount++;
  306.               escapeCount = escapeCount % 2;
  307.             }
  308.               if (ch == '"' && escapeCount == 0)
  309.             break; /* out of reading string */
  310.                     }
  311.         }
  312.           break; /* we are done */
  313.             }
  314.       else if ((isdigit(ch) || ch == '-' || ch == '.') || /* number */
  315.            (ch == ':')) /* symbol */
  316.         { while (!isspace(ch)) /* just read till there is white space */
  317.         { ch = getc(file);
  318.           if (ch == EOF)
  319.             return(EOF);
  320.         }
  321.           break; /* we are done */
  322.         }
  323.       else if ((ch == '#') || /* array */
  324.            (ch == '(')) /* struct or list */
  325.         { long parenCount = 1;
  326.           if (ch == '#')    
  327.         ch = getc(file); /* read in the '(' so we can think of it
  328.                     as a list */
  329.           while (parenCount > 0)
  330.         { ch = getc(file);
  331.           if (ch == EOF)
  332.             return(EOF);
  333.           else if (ch == '"')
  334.             { /* start of a string, it may contain parens, 
  335.              so we will have to skip it */
  336.               ungetc(ch,file);
  337.               SkipObject(file);
  338.             }
  339.           else if (ch == '(') /* entering a new structure */    
  340.             parenCount++;
  341.           else if (ch == ')') /* leaving a structure */
  342.             parenCount--;
  343.         }
  344.           break; /* we are done */
  345.         }
  346.     }
  347.     }
  348.  
  349.   return(true);
  350. }
  351.  
  352. long ReadLong(file,answer)
  353. FILE* file;
  354. long* answer;
  355. /* reads a long int a file returns true if successful, false otherwise */
  356. {
  357.   long ch;
  358.   long state = BEFORE;
  359.   boolean isNegative = false;
  360.   long count = 0;
  361.   
  362.   *answer = 0;
  363.   
  364.   while(TRUE){
  365.     ch = getc(file);
  366.     if (ch == EOF){
  367.       break;            /* we are done */
  368.     }
  369.     else if (isdigit(ch)){
  370.       if(state == BEFORE){
  371.     state = DURING;
  372.       }
  373.       count++;
  374.       if(count == 12){
  375.     /* then we have an error in the file, 32 bit numbers can not be more
  376.        than 10 digits long */
  377.     return(false);
  378.       }
  379.       *answer = *answer * 10 + (ch - '0');
  380.     }
  381.     else if (ch == '-') {
  382.       if (isNegative)
  383.     /* then we have an error since there should be only one - in a number */
  384.     return(false);
  385.       if (state == BEFORE) {
  386.     /* we are ok since the - must come before any digits */
  387.     isNegative = true;
  388.     state = DURING;
  389.       }
  390.       else {
  391.     ungetc(ch,file);
  392.     break;            /* we are done */
  393.       }
  394.     }
  395.     else if(ch == ')' && (state == DURING)){
  396.       ungetc(ch, file);
  397.       return(true);        /* we are done */
  398.     }
  399.     else if(!isspace(ch)){
  400.       /* then we have an error since it should be a digit or a space */
  401.       return(false);
  402.     }
  403.     /* we do not have an digit */
  404.     else if(state == DURING){
  405.       ungetc(ch, file);
  406.       break;            /* we are done */
  407.     }
  408.     /* otherwise we are still before the start */
  409.   }
  410.   
  411.   if (isNegative)
  412.     *answer *= -1;
  413.   return(true);
  414. }
  415.  
  416. long ReadDouble(file,answer)
  417. FILE* file;
  418. double* answer;
  419. {
  420.   /* XXX this routine needs to deal with negative numbers! */
  421.   long ch;
  422.   long state = BEFORE;
  423.   long count = 0;
  424.   long decimal_count = 0;
  425.   
  426.   *answer = 0.0;
  427.   
  428.   while(TRUE){    
  429.     ch = getc(file);
  430.     if (ch == EOF){
  431.       return(true);
  432.     }
  433.     else if (ch == '.'){
  434.       decimal_count ++;
  435.     }
  436.     else if (isdigit(ch)){
  437.       if(state == BEFORE){
  438.     state = DURING;
  439.       }
  440.       count++;
  441.       if(count == 12){
  442.     /* then we have an error in the file, 32 bit numbers can not be more
  443.        than 10 digits long */
  444.     return(false);
  445.       }
  446.       if (decimal_count == 0){
  447.     *answer = *answer * 10 + (ch - '0');
  448.       }
  449.       else{            /* then we are in the fraction part */
  450.     double fraction = (ch - '0');
  451.     long internal_count;
  452.     for(internal_count = 0; internal_count < decimal_count; 
  453.         internal_count++){
  454.       fraction = fraction / 10.0;
  455.     }
  456.     *answer = *answer + fraction;
  457.     decimal_count++;
  458.       }
  459.     }
  460.     else if(!isspace(ch)){
  461.       /* then we have an error since it should be a digit or a space */
  462.       return(false);
  463.     }
  464.     /* we do not have an digit */
  465.     else if(state == DURING){
  466.       ungetc(ch, file);
  467.       return(true);        /* we are done */
  468.     }
  469.     /* otherwise we are still before the start */
  470.   }
  471. }
  472.  
  473. static Boolean issymbolchar _AP((long ch));
  474.  
  475. static 
  476. Boolean issymbolchar(ch)
  477. long ch;
  478. /* reads a symbol from a file and put it in the string argument.
  479.  * The string_size argument is used to make sure the string is not 
  480.  * overflowed.
  481.  */
  482. {
  483.   return(!( isspace(ch) || ch == ')' || ch == '(' || ch == EOF));
  484. }
  485.  
  486. /* reads a symbol from a file */
  487. long ReadSymbol(string,file,string_size)
  488. char* string;
  489. FILE* file;
  490. long string_size;
  491. {
  492.   long ch;
  493.   long state = BEFORE;
  494.   long position = 0;
  495.   
  496.   while(TRUE){
  497.     ch = getc(file);
  498.     if((state == BEFORE) && (ch == ')'))
  499.       return(END_OF_STRUCT_OR_LIST);
  500.     if(issymbolchar((long)ch)){    /* we are in a symbol */
  501.       if(state == BEFORE)
  502.     state = DURING;
  503.       string[position] = ch;
  504.       position++;
  505.       if(position >= string_size){
  506.     string[string_size - 1] = '\0';
  507.     return(FALSE);
  508.       }
  509.     }
  510.     /* we do not have an symbol character. we are done */
  511.     else if((state == DURING) || ch == EOF){
  512.       if(ch != EOF) ungetc(ch, file);
  513.       string[position] = '\0';
  514.       return(TRUE);        /* we are done */
  515.     }
  516.     /* otherwise we are still before the start of the symbol */
  517.   }
  518. }
  519.  
  520. long ReadEndOfListOrStruct(file)
  521. FILE* file;
  522. {
  523.   long ch;
  524.   while(TRUE){
  525.     ch = getc(file);
  526.     if (EOF == ch) 
  527.       return(FALSE);
  528.     else if(')' == ch) 
  529.       return(TRUE);
  530.     else if(!isspace(ch)) 
  531.       return(FALSE);
  532.   }
  533. }
  534.  
  535. /* reads a string from a file */
  536. long ReadString(string,file,string_size)
  537. char* string;
  538. FILE* file;
  539. long string_size;
  540. {
  541.   long ch;
  542.   long state = BEFORE;
  543.   long position = 0;
  544.   string[0] = '\0';  /* initialize to nothing */
  545.   
  546.   while(TRUE){
  547.     ch = getc(file);
  548.     if((state == BEFORE) && (ch == '\"'))
  549.       state = DURING;
  550.     else if (EOF == ch){
  551.       string[position] = '\0';
  552.       return(FALSE);
  553.     }
  554.     else if ((state == BEFORE) && (ch == ')'))
  555.       return(END_OF_STRUCT_OR_LIST);
  556.     else if ((state == DURING) && (ch == '\\'))
  557.       state = QUOTE; /* do nothing */
  558.     else if ((state == DURING) && (ch == '"')){    
  559.       string[position] = '\0';
  560.       return(TRUE);
  561.     }
  562.     else if ((state == QUOTE) || (state == DURING)){
  563.             if(state == QUOTE)
  564.                 state = DURING;
  565.             string[position] = ch;
  566.             position++;
  567.             if(position >= string_size){
  568.                 string[string_size - 1] = '\0';
  569.                 return(FALSE);
  570.             }
  571.         }
  572.         /* otherwise we are still before the start of the string */
  573.     }
  574. }
  575.  
  576. /* returns TRUE if it is the start of a struct
  577.  * returns END_OF_STRUCT_OR_LIST if it is a ')'
  578.  * returns FALSE if it is something unexpected
  579.  */
  580. long ReadStartOfStruct(name,file)
  581. char* name;
  582. FILE* file;
  583. {
  584.   long ch;
  585.   long state = BEFORE;
  586.     
  587.   name[0] = '\0';
  588.     
  589.   while(TRUE){
  590.     ch = getc(file);
  591.     if((state == BEFORE) && (ch == '#'))
  592.       state = HASH;
  593.     if((state == BEFORE) && (ch == '('))
  594.       state = DURING;
  595.     else if((state == BEFORE) && (ch == ')'))
  596.       return(END_OF_STRUCT_OR_LIST);
  597.     else if((state == BEFORE) && !isspace(ch))
  598.       return(FALSE);        /* we have a problem */
  599.     else if(state == HASH){
  600.       if (ch == 's')
  601.     state = S;
  602.       else{
  603.     fprintf(stderr,"Expected an 's' but got an %c\n", ch);
  604.     return(FALSE);
  605.       }
  606.     }
  607.     else if(state == S){
  608.       if (ch == '(')
  609.     state = DURING;
  610.       else{
  611.     fprintf(stderr,"Expected an '(' but got an an %c\n",ch);
  612.     return(FALSE);
  613.       }
  614.     }
  615.     else if(state == DURING){
  616.       return(ReadSymbol(name, file, MAX_SYMBOL_SIZE));
  617.     }
  618.   }
  619. }
  620.  
  621. /* returns TRUE if it is the right start of a struct,
  622.  * returns END_OF_STRUCT_OR_LIST if it is the end of a list,
  623.  * returns FALSE if it is something weird
  624.  */
  625. long CheckStartOfStruct(name,file)
  626. char* name;
  627. FILE* file;
  628. {
  629.   char temp_string[MAX_SYMBOL_SIZE];
  630.   long result = ReadStartOfStruct(temp_string, file);
  631.   if(result == END_OF_STRUCT_OR_LIST)
  632.     return(END_OF_STRUCT_OR_LIST);
  633.   else if(result == FALSE)
  634.     return(FALSE);
  635.   else if(0 == strcmp(temp_string, name))
  636.     return(TRUE);
  637.   else 
  638.     return(FALSE);
  639. }
  640.  
  641. /* reads an any.  an any with no bytes allocated.  The right number of bytes
  642.  * will be malloc'ed while reading
  643.  */
  644. long ReadAny(destination,file)
  645. any* destination;
  646. FILE* file;
  647. {
  648.   char temp_string[MAX_SYMBOL_SIZE];
  649.     
  650.   destination->size = 0; /* initialize so that if an error happens 
  651.                 it does not blow up */
  652.   if(FALSE == CheckStartOfStruct("any", file)){
  653.     fprintf(stderr,"An 'any' structure was not read from the disk");
  654.     return(FALSE);
  655.   }
  656.     
  657.   while(TRUE){
  658.     long check_result;
  659.     check_result = ReadSymbol(temp_string, file, MAX_SYMBOL_SIZE);
  660.     if(FALSE == check_result) 
  661.       return(FALSE);
  662.     if(END_OF_STRUCT_OR_LIST == check_result) 
  663.       return(TRUE);
  664.         
  665.     if(0 == strcmp(temp_string, ":size")) {
  666.       long    size;
  667.       ReadLong(file,&size);
  668.       destination->size = (unsigned long)size;
  669.     }
  670.     else if(0 == strcmp(temp_string, ":bytes")){
  671.       long result;
  672.       /* the size must have been read in by now */
  673.       destination->bytes = (char*)s_malloc(destination->size);
  674.       if(NULL == destination->bytes){
  675.     fprintf(stderr,
  676.         "Error on reading file. Malloc ran out of memory in an ANY");
  677.     return(FALSE);
  678.       }
  679.       result = Read8BitArray(destination->bytes, file, destination->size);
  680.       if(FALSE == result)
  681.     return(FALSE);
  682.     }
  683.     else{
  684.       fprintf(stderr,"Unknown keyword for ANY %s\n", temp_string);
  685.       return(FALSE);
  686.     }
  687.   }
  688. }
  689.  
  690. /* this does not need the length, but it will probably know it in all cases */
  691. long Read8BitArray(destination,file,length)
  692. char* destination;
  693. FILE* file;
  694. long length;
  695. {
  696.   /* arrays start with #( */
  697.   long ch;
  698.   long state = BEFORE;
  699.   while(TRUE){
  700.     ch = getc(file);
  701.     if((state == BEFORE) && ((ch == '#') || (ch == '('))) {
  702.       if (ch == '(') state = DURING;
  703.       else state = HASH;
  704.     }
  705.     else if((state == BEFORE) && !isspace(ch)){
  706.       fprintf(stderr,"error in reading array.  Expected # and got %c", ch);
  707.       return(FALSE);
  708.     }
  709.     else if(state == HASH){
  710.       if (ch == '(')
  711.     state = DURING;
  712.       else{
  713.     fprintf(stderr,"Expected an '(' but got an %c\n", ch);
  714.     return(FALSE);
  715.       }
  716.     }
  717.     else if(state == DURING){
  718.       long i;
  719.       ungetc(ch, file);
  720.       for(i = 0; i < length; i++){
  721.     long value;
  722.     if(ReadLong(file,&value) == false){ /* then it error'ed */
  723.       fprintf(stderr,"Error in reading a number from the file.");
  724.       return(FALSE);
  725.     }
  726.     if(value > 255){    /* then we have read a non-char */
  727.       fprintf(stderr,"Error in reading file.  Expected a byte in an ANY, but got %ld", value);
  728.       return(FALSE);
  729.     }
  730.     destination[i] = (char)value;
  731.       }
  732.       if(FALSE == ReadEndOfListOrStruct(file)){
  733.     fprintf(stderr,"array was wrong length");
  734.     return(FALSE);
  735.       }
  736.       return(TRUE);
  737.     }
  738.   }
  739. }
  740.             
  741.  
  742. Boolean
  743. readAbsoluteTime(atime,file)
  744. struct tm* atime;
  745. FILE* file;
  746. {
  747.   if (CheckStartOfStruct("absolute-time",file) == FALSE)
  748.     return(false);
  749.           
  750.   while (true)
  751.     { long result;
  752.       long val;
  753.       char temp_string[MAX_SYMBOL_SIZE + 1];
  754.      
  755.       result = ReadSymbol(temp_string,file,MAX_SYMBOL_SIZE);
  756.      
  757.       if (result == END_OF_STRUCT_OR_LIST)
  758.     break;
  759.       else if (result == false)
  760.     return(false);
  761.                   
  762.       if (strcmp(temp_string,":second") == 0)
  763.     { if (ReadLong(file,&val) == false)
  764.         return(false);
  765.       atime->tm_sec = val;
  766.     }
  767.  
  768.       else if (strcmp(temp_string,":minute") == 0)
  769.     { if (ReadLong(file,&val) == false)
  770.         return(false);
  771.       atime->tm_min = val;
  772.     }
  773.  
  774.       else if (strcmp(temp_string,":hour") == 0)
  775.     { if (ReadLong(file,&val) == false)
  776.         return(false);
  777.       atime->tm_hour = val;
  778.     }
  779.  
  780.       else if (strcmp(temp_string,":mday") == 0)
  781.     { if (ReadLong(file,&val) == false)
  782.         return(false);
  783.       atime->tm_mday = val;
  784.     }
  785.  
  786.       else if (strcmp(temp_string,":month") == 0)
  787.     { if (ReadLong(file,&val) == false)
  788.         return(false);
  789.       atime->tm_mon = val;
  790.     }
  791.  
  792.       else if (strcmp(temp_string,":year") == 0)
  793.     { if (ReadLong(file,&val) == false)
  794.         return(false);
  795.       atime->tm_year = val;
  796.     }
  797.       
  798.       else
  799.     SkipObject(file);
  800.  
  801.     }
  802.  
  803.   return(true);
  804. }
  805.